<?php

// Allow easy console colloring
require_once 'Console/Color.php';

/**
* Script for creating and loading database
*/
 
// Initialize the application path and autoloading


$dirname = getcwd();

defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath($dirname . '/../www/application'));

set_include_path(implode(PATH_SEPARATOR, array(
    APPLICATION_PATH . '/../www/library',
    get_include_path(),
)));

require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
 
// Define some CLI options
$getopt = new Zend_Console_Getopt(array(
   'table|t=w' => 'Table name to create model files out of',
   'env|e-s'   => 'Application environment for which to create model s(defaults to development)',
   'help|h'    => 'Help -- usage message',
));

try
{
    $getopt->parse();
}
catch (Zend_Console_Getopt_Exception $e)
{
    // Bad options passed: report usage
    echo $e->getUsageMessage();
    return false;
}
 
// If help requested, report usage message
if ($getopt->getOption('h'))
{
    echo $getopt->getUsageMessage();
    return true;
}
 
// Initialize values based on presence or absence of CLI options
$env      = $getopt->getOption('e');
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (null === $env) ? 'development' : $env);
 
$tableName = $getopt->getOption('t');


$pAppConfig = new Zend_Config_Ini(
   APPLICATION_PATH . '/configs/application.ini',
   APPLICATION_ENV
);

$pRegistry = Zend_Registry::getInstance();
$pRegistry->set('config', $pAppConfig);


// Initialize Zend_Application
$pApplication = new Zend_Application(
    APPLICATION_ENV
);
$pApplication->setOptions($pAppConfig->toArray());

echo PHP_EOL . Console_Color::convert('%gUsing APPLICATION_ENV = ' . APPLICATION_ENV . '%n') . PHP_EOL . PHP_EOL;

// Try to include overrides.
if(is_file(APPLICATION_PATH . '/../../scripts/overrides.php'))
{
   require_once(APPLICATION_PATH . '/../../scripts/overrides.php');
}

// Initialize and retrieve DB resource
$bootstrap = $pApplication->getBootstrap();
$bootstrap->bootstrap('db');
$dbAdapter = $bootstrap->getResource('db');
//$dbAdapter->init();

if($dbAdapter->getPdoType() === 'mysql')
   $generator = new MysqlGenerator($dbAdapter, $tableName);
else if($dbAdapter->getPdoType() === 'pgsql')
   $generator = new PgsqlGenerator($dbAdapter, $tableName);
else
   echo "Unsupported db type: " . $dbAdapter->getPdoType() . PHP_EOL;

$generator->createModelsAndProcedures();

class Generator 
{
   protected $_dbAdapter;
   protected $_tableName;
   protected $_sqlProcString = '';
   protected $_sqlDropString = '';
   protected $_data;

   public function __construct($dbAdapter, $tableName = null)
   {
      $this->_dbAdapter = $dbAdapter;
      $this->_tableName = $tableName;
      $this->_data = json_decode(file_get_contents('dbSchema.json'), true);
   }

   protected function getTableNames()
   {
      $tableName = [];
      foreach($this->_data as $tName => $tData)
      {
         $tableName[] = $tName;
      }

      return $tableName;
   }

   protected function createTableDesc($tableName)
   {
      foreach ($this->_data[$tableName] as $row => $col)
      {
         if ($row === "Indexes") continue;
         $tableTemp = [];
         foreach ($col as $key => $val)
         {
            $tableTemp[] = $val;
         }
      }
      return $tableTemp;
   }

   public function createModelsAndProcedures()
   {
      if($this->_tableName !== null)
      {
         $this->createModelAndProcedures($tableName);
      }
      else
      {
         foreach($this->getTableNames() as $tableName)
            $this->createModelAndProcedures($tableName);
      }

      $this->saveProceduresToFile();
   }

   public function createModelAndProcedures($tableName)
   {
      echo Console_Color::convert("%pProcessing table \"$tableName\"...%n") . PHP_EOL;
      $tableDescription = $this->getTableDesc($tableName);
      $tableDesc = $tableDescription['desc'];
      $versioned = $tableDescription['versioned'];
      $overrides = $this->getSqlOverride($tableName);
      $this->makeSqlDropProcedures($tableName, $tableDesc, $overrides);
      $this->makeSqlProcedures($tableName, $tableDesc, $versioned, $overrides);

      if($this->createModelsSupported())
      {
         $strPad = $tableDescription['pad'];
         $this->createModels($tableName, $tableDesc, $strPad, $versioned, $overrides);
      }
   }

   protected function addProcedure($str)
   {
      if(!strlen($str))
         return;

      $this->_sqlProcString .= $str;
      try
      {
      $this->_dbAdapter->query($this->fixProcStringForQuery($str));
      }
      catch (Exception $e) { echo $e . PHP_EOL; echo $str . PHP_EOL; exit; } }

   protected function addDropProcedure($str)
   {
      if(!strlen($str))
         return;

      $this->_sqlDropString .= $str;
      $this->_dbAdapter->query($this->fixProcStringForQuery($str));
   }

   protected function createModels($tableName, $tableDesc, $strPad, $versioned, $overrides)
   {
      $dbModelString = $this->makeDbModelFileString($tableName, $tableDesc, $strPad, $versioned, $overrides);
      $modelString = $this->makeModelFileString($tableName, $tableDesc, $strPad);
      $modelMapperString = $this->makeModelMapperFileString($tableName, $tableDesc, $strPad);
      $dbModelFilePath = APPLICATION_PATH . '/models/DbTable/' . ucfirst($this->makeLowercamelcase($tableName))  . '.php';
      $modelFilePath = APPLICATION_PATH . '/models/' . ucfirst($this->makeLowercamelcase($tableName))  . '.php';
      $modelMapperFilePath = APPLICATION_PATH . '/models/' . ucfirst($this->makeLowercamelcase($tableName))  . 'Mapper.php';

      $newContent = $this->switchOutContent($dbModelFilePath, $dbModelString, true, true);
      if(is_file($dbModelFilePath))
      {
         //echo Console_Color::convert("%y\tUpdated DbModel file.%n\n");
      }
      else
      {
         echo Console_Color::convert("%g\tCreated DbModel file.%n\n");
      }
      file_put_contents($dbModelFilePath, $newContent);

      $newContent = $this->switchOutContent($modelFilePath, $modelString, true);
      if(is_file($dbModelFilePath))
      {
         //echo Console_Color::convert("%y\tUpdated Model file.%n\n");
      }
      else
      {
         echo Console_Color::convert("%g\tCreated Model file.%n\n");
      }
      file_put_contents($modelFilePath, $newContent);

      $newContent = $this->switchOutContent($modelMapperFilePath, $modelMapperString);
      if(is_file($dbModelFilePath))
      {
         //echo Console_Color::convert("%y\tUpdated ModelMapper file.%n\n");
      }
      else
      {
         echo Console_Color::convert("%g\tCreated ModelMapper file.%n\n");
      }
      file_put_contents($modelMapperFilePath, $newContent);
   }

   protected function makeSqlProcedures($tableName, $tableDesc, $versioned, $overrides)
   {
      $this->addProcedure($this->makeSqlInsert($tableName, $tableDesc, $versioned, $overrides));
      $this->addProcedure($this->makeSqlUpdate($tableName, $tableDesc, $versioned, $overrides));
      $this->addProcedure($this->makeSqlDelete($tableName, $tableDesc, $versioned, $overrides));
      if(isset($overrides["Increment"]))
      {
         foreach($overrides["Increment"] as $incrementProc)
            $this->addProcedure($this->makeSqlIncrement($tableName, $tableDesc, $versioned, $overrides, $incrementProc["Where"], $incrementProc["Column"]));
      }
   }

   protected function makeSqlDropProcedures($tableName, $tableDesc, $overrides)
   {
      $this->addDropProcedure($this->makeDropProcedure($tableName, $tableDesc, "Insert"));
      $this->addDropProcedure($this->makeDropProcedure($tableName, $tableDesc, "Update"));
      $this->addDropProcedure($this->makeDropProcedure($tableName, $tableDesc, "Delete"));
      if(isset($overrides["Increment"]))
      {
         foreach($overrides["Increment"] as $incrementProc)
            $this->addDropProcedure($this->makeDropProcedure($tableName, $tableDesc, "IncrementBy" . $incrementProc["Where"]));
      }
   }

   protected function createModelsSupported()
   {
      return false;
   }

   protected function fixProcStringForQuery($procString)
   {
      return $procString;
   }

   protected function getParamList($tableDesc, array $include = null, array $exclude = null)
   {
      $paramList = array();
      foreach($tableDesc as $column)
      {
         $field = $column["Field"];
         if($include && !in_array($field, $include))
            continue;
         if($exclude && in_array($field, $exclude))
            continue;

         $paramList[] = "\"" . $field . "\" " . $this->getSQLType($column);
      }
      return $paramList;
   }

   protected function getDeclareList($tableDesc, array $include = null, array $exclude = null)
   {
      $declareList = array();
      foreach($tableDesc as $column)
      {
         $field = $column["Field"];
         if($include && !in_array($field, $include))
            continue;

         if($exclude && in_array($field, $exclude))
            continue;

         $declareList[] = "\"" . $field . "\" " . $this->getSQLType($column);
      }
      
      return $declareList;
   }

   protected function getFieldList($tableDesc, $tableName = null, array $include = null, array $exclude = null)
   {
      $fieldList = array();
      foreach($tableDesc as $column)
      {
         $field = $column['Field'];
         if($include && !in_array($field, $include))
            continue;

         if($exclude && in_array($field, $exclude))
            continue;

         if($tableName)
            $field = '"' . $tableName . '"."' . $column['Field'] . '"';
         else
            $field = '"' . $column['Field'] . '"';
         $fieldList[] = $field;
      }

      return $fieldList;
   }

   protected function getFieldAssignList($fieldList, $varList)
   {
      $fieldAssignList = array();
      $len = count($fieldList);
      for($i = 0; $i < $len; ++$i)
      {
         if($varList[$i] === '"Id"' || $varList[$i] === '"ObjectVersion"') # || $varList[$i] === '"CreatedBy"' || $varList[$i] === '"Created"')
            continue;

         $fieldAssignList[] = $fieldList[$i] . ' = ' . $varList[$i];
      }

      return $fieldAssignList;
   }

   protected function getStageStr($tableName, $tableDesc)
   {
      $varList = $this->getFieldList($tableDesc);

      $str = '      ' . $this->getCallStr() . ' ' . $tableName . 'Stage(' . implode(', ', $varList) . ');' . PHP_EOL;
      return $str;
   }

   protected function makeDropProcedure($tableName, $tableDesc, $funcName)
   {
      $paramList = $this->getParamList($tableDesc);
      return 'DROP FUNCTION IF EXISTS ' . $tableName . $funcName . '(' . implode(', ', $paramList) . ');' . PHP_EOL;
   }

   protected function getProcedureBeginStr($tableName, $funcName, $paramList, $returnType = null, $declareList = array())
   {
      $str  = 'CREATE OR REPLACE FUNCTION ' . $tableName . $funcName . '(' . implode(', ', $paramList) . ')' . ' RETURNS ' . ($returnType ? $returnType : 'VOID') . ' AS $$' . PHP_EOL;
      $str .= '#variable_conflict use_variable' . PHP_EOL;
      $str .= $this->getDeclareStr($declareList);
      $str .= '   BEGIN' . PHP_EOL;
      return $str;
   }

   protected function getDeclareStr($declareList)
   {
      $str = '   DECLARE' . PHP_EOL;
      $str .= '      ' . implode(';' . PHP_EOL . '      ', $declareList);
      if(count($declareList))
         $str .= ';' . PHP_EOL;

      return $str;
   }

   protected function getProcedureEndStr()
   {
      $str = "   END;" . PHP_EOL . "$$ LANGUAGE 'plpgsql';" . PHP_EOL;
      $str .= PHP_EOL;
      return $str;
   }

   protected function getNowFuncStr()
   {
      return "now()";
   }

   protected function getUuidStr()
   {
      return "unhex(replace(uuid_in(md5(now()::text)::cstring)::text, '-', ''))";
   }

   protected function getSetStr($varName, $value, $quoteIntoVarName = true)
   {
      return ($quoteIntoVarName ? '"' : '') . $varName . ($quoteIntoVarName ? '"' : '') . ' = ' . $value;
   }

   protected function getSetSessionVarStr($varName, $value)
   {
      return "SELECT set_config('session." . $varName . "', " . $value . ', false)';
   }

   protected function getCurrentUserStr()
   {
      return "(SELECT current_setting('session.CURRENT_USER'))";
   }

   protected function canReturnOnInsert()
   {
      return true;
   }

   protected function canReturnValues()
   {
      return true;
   }

   //Truncate is not transaction safe.
   protected function getTruncateTableStr($tableName)
   {
      return 'DELETE FROM "' . $tableName . '";' . PHP_EOL;
   }

   protected function getCallStr()
   {
      return "PERFORM";
   }

   protected function hasCommaSeparatedUpdateTables()
   {
      return false;
   }

   protected function coalesceNeedsCast()
   {
      return true;
   }

   protected function getProcLockString(array $list)
   {
      foreach ($list as & $x)
         $x = '"' . $x . '"';

      return 'LOCK TABLE ' . implode(', ', $list); //SELECT GET_LOCK(' . implode('__', $list) . '__LOCK' . ')';
   }

   protected function getProcUnlockString(array $list)
   {
      return ''; //SELECT RELEASE_LOCK()';
   }

   protected function getCopyStr($sourceTable, $targetTable, $tableDesc)
   {
      $varList = $this->getFieldList($tableDesc);
      $fromList = $this->getFieldList($tableDesc, $sourceTable);
      unset($varList[0]);
      unset($fromList[0]);
      return 'INSERT INTO "' . $targetTable . '" (' . implode(', ', $varList) . ') SELECT ' . implode(', ', $fromList) . ' FROM "' . $sourceTable . '";' . PHP_EOL;
   }

   protected function tableHasColumn($tableDesc, $field)
   {
      foreach($tableDesc as $column)
      {
         if($column["Field"] === $field)
            return true;
      }

      return false;
   }

   protected function getColumnType($tableDesc, $field)
   {
      foreach($tableDesc as $column)
      {
         if($column["Field"] !== $field)
            continue;

         return $this->getSQLType($column);
      }

      return null;
   }

   protected function subQueryCanJoinOnOuterTables()
   {
      return true;
   }

   protected function makeSqlInsert($tableName, $tableDesc, $versioned, $overrides)
   {
      $paramList = $this->getParamList($tableDesc);
      $varList = $this->getFieldList($tableDesc);
      $valuesList = $this->getFieldList($tableDesc, null, null, array('Id'));
      array_unshift($valuesList, 'DEFAULT');
      $guid = false;
      $created = false;
      $createdBy = false;
      foreach($tableDesc as $column)
      {
         if($column["Field"] === "Guid")
            $guid = true;
         else if($column["Field"] === "Created")
            $created = true;
         else if($column["Field"] === "CreatedBy")
            $createdBy = true;
      }

      $str = $this->getProcedureBeginStr($tableName, 'Insert', $paramList, $this->canReturnValues() ? 'numeric' : null);
      if($versioned)
         $str .= '      ' . $this->getSetStr("ObjectVersion", 1) . ';' . PHP_EOL;
      if($created)
         $str .= '      ' . $this->getSetStr("Created", $this->getNowFuncStr()) . ';' . PHP_EOL;
      if($createdBy)
         $str .= '      ' . $this->getSetStr("CreatedBy", $this->getCurrentUserStr()) . ';' . PHP_EOL; 
      if($guid)
         $str .= '      ' . $this->getSetStr("Guid", $this->getUuidStr()) . ';' . PHP_EOL;
      $str .= '      INSERT INTO "' . $tableName . '" (' . implode(', ', $varList) . ') VALUES (' . implode(', ', $valuesList) . ')' . ($this->canReturnOnInsert() ? ' RETURNING "' . $tableName . '"."Id" INTO "Id"' : '') . ';' . PHP_EOL;
      if(!$this->canReturnOnInsert())
         $str .= '      ' . $this->getSetStr("Id","LAST_INSERT_ID()") . ';' . PHP_EOL;

      if($versioned)
      {
         $str .= '      UPDATE "' . $tableName . '" SET "PrimeId" = "Id" WHERE "' . $tableName . '"."Id" = "Id";' . PHP_EOL;
         $str .= '      ' . $this->getSetStr("PrimeId", '"Id"') . ';' . PHP_EOL;
         $str .= '      ' . $this->getSetStr("Operation", "'Create'") . ';' . PHP_EOL;
         $str .= '      INSERT INTO "' . $tableName . '" (' . implode(', ', $varList) . ') VALUES (' . implode(', ', $valuesList) . ')' . ';' . PHP_EOL;
      }

      if($this->canReturnValues())
         $str .= '      RETURN "Id";' . PHP_EOL;
      else
         $str .= '      ' . $this->getSetSessionVarStr("@RET", '"Id"') . ';' . PHP_EOL;
      $str .= $this->getProcedureEndStr();
      return $str;
   }

   protected function makeSqlUpdate($tableName, $tableDesc, $versioned, $overrides)
   {
      $paramList = $this->getParamList($tableDesc);
      $varList = $this->getFieldList($tableDesc, null, null, array('Id', 'Guid'));
      $valuesList = $this->getFieldList($tableDesc, null, null, array('Id'));
      $fieldList = $this->getFieldList($tableDesc, null, null, array('Id', 'Guid'));
      $fieldAssignList = $this->getFieldAssignList($fieldList, $varList);

      $str  = $this->getProcedureBeginStr($tableName, 'Update', $paramList);
      if($versioned)
      {
         $str .= '      ' . $this->getSetStr("Created", $this->getNowFuncStr()) . ';' . PHP_EOL;
         $str .= '      ' . $this->getSetStr("CreatedBy", $this->getCurrentUserStr()) . ';' . PHP_EOL;
      }
      $str .= '      UPDATE "' . $tableName . '" SET ' . implode(', ', $fieldAssignList) . ' WHERE "' . $tableName . '"."Id" = "Id";' . PHP_EOL;
      if($this->tableHasColumn($tableDesc, "Guid"))
      {
         $str .= '      UPDATE "' . $tableName . '" SET "Guid" = "Guid" WHERE "' . $tableName . '"."Id" = "Id";' . PHP_EOL;
      }
      if($versioned)
      {
         $str .= '      ' . $this->getSetStr("Operation", "'Modify'") . ';' . PHP_EOL;
         $fields = array(array("Field" => 'ObjectVersion'));
         if($this->tableHasColumn($tableDesc, "Guid"))
            $fields[] = array("Field" => 'Guid');
         $fieldList = $this->getFieldList($fields, $tableName);
         $varList = $this->getFieldList($fields);
         $str .= '      UPDATE "' . $tableName . '" SET "ObjectVersion" = "ObjectVersion" + 1 WHERE "' . $tableName . '"."Id" = "Id";' . PHP_EOL;
         $str .= '      SELECT ' . implode(', ', $fieldList) . ' INTO ' . implode(', ', $varList) . ' FROM "' . $tableName . '" WHERE "' . $tableName . '"."Id" = "Id";' . PHP_EOL;

         $str .= '      INSERT INTO "' . $tableName . '" (' . implode(', ', $valuesList) . ') VALUES (' . implode(', ', $valuesList) . ')' . ';' . PHP_EOL;
      }

      $str .= $this->getProcedureEndStr();
      return $str;
   }

   protected function makeSqlDelete($tableName, $tableDesc, $versioned, $overrides)
   {
      $varList = $this->getFieldList($tableDesc);
      $valuesList = $this->getFieldList($tableDesc, null, null, array('Id'));
      $fieldList = $this->getFieldList($tableDesc, $tableName);
      $paramList = $this->getParamList($tableDesc, array('Id'));
      $declareList = $this->getDeclareList($tableDesc, null, array('Id'));

      $str  = $this->getProcedureBeginStr($tableName, 'Delete', $paramList, null, $declareList);
      $str .= '      SELECT ' . implode(', ', $fieldList) . ' INTO ' . implode(', ', $varList) . ' FROM "' . $tableName . '" WHERE "' . $tableName . '"."Id" = "Id";' . PHP_EOL;
      $str .= '      IF("Id" IS NOT NULL)' . PHP_EOL;
      $str .= '      THEN' . PHP_EOL;
      $str .= '         DELETE FROM "' . $tableName . '" WHERE "' . $tableName . '"."Id" = "Id";' . PHP_EOL;
      if($versioned)
      {
         $str .= '         ' . $this->getSetStr("Created", $this->getNowFuncStr()) . ';' . PHP_EOL;
         $str .= '         ' . $this->getSetStr("CreatedBy", $this->getCurrentUserStr()) . ';' . PHP_EOL;
         $str .= '         ' . $this->getSetStr("Operation", "'Delete'") . ';' . PHP_EOL;
         $str .= '         ' . $this->getSetStr("ObjectVersion", '"ObjectVersion" + 1') . ';' . PHP_EOL;

         $str .= '         INSERT INTO "' . $tableName . '" (' . implode(', ', $valuesList) . ') VALUES (' . implode(', ', $valuesList) . ')' . ';' . PHP_EOL;
      }

      $str .= '      END IF;' . PHP_EOL;
      $str .= $this->getProcedureEndStr();
      return $str;
   }

   protected function makeSqlIncrement($tableName, $tableDesc, $versioned, $overrides, $where, $column)
   {
      $paramList = $this->getParamList($tableDesc, array($where));
      $declareList = $this->getDeclareList($tableDesc, null, array($where));
      $fieldList = $this->getFieldList($tableDesc, $tableName, null, array($column, $where));
      $intoList = $this->getFieldList($tableDesc, null, null, array($column, $where));
      $varList = $this->getFieldList($tableDesc);
      $valuesList = $this->getFieldList($tableDesc, null, null, array('Id'));
      foreach($valuesList as &$value)
         if($value === '"' . $column . '"')
            $value = 1;

      array_unshift($valuesList, 'DEFAULT');

      $str = $this->getProcedureBeginStr($tableName, 'IncrementBy' . $where, $paramList, $this->canReturnValues() ? 'numeric' : null, $declareList);
      $str .= '      SELECT ' . implode(', ', $fieldList) . ' INTO ' . implode(', ', $intoList) . ' FROM "' . $tableName . '" WHERE "' . $tableName . '"."' . $where . '" = "' . $where . '";' . PHP_EOL;
      $str .= '      IF("Id" IS NOT NULL)' . PHP_EOL;
      $str .= '      THEN' . PHP_EOL;
      if($this->canReturnOnInsert())
      {
         $str .= '         UPDATE "' . $tableName . '" SET "' . $column . '" = "' . $tableName . '"."' . $column . '" + 1 WHERE "' . $tableName . '"."' . $where . '" = "' . $where . '" RETURNING "' . $tableName . '"."' . $column . '" INTO "' . $column . '";' . PHP_EOL;
      }
      else
      {
         $str .= '         UPDATE "' . $tableName . '" SET "' . $column . '" = LAST_INSERT_ID("' . $tableName . '"."' . $column . '" + 1) WHERE "' . $tableName . '"."' . $where . '" = "' . $where . '";' . PHP_EOL;
         $str .= '         ' . $this->getSetStr($column, "LAST_INSERT_ID()") . ';' . PHP_EOL;
      }
      $str .= '      ELSE' . PHP_EOL;
      $str .= '         ' . $this->getProcLockString(array($tableName, $tableName . 'S')) . ';' . PHP_EOL;
      $str .= '         IF("Id" IS NOT NULL)' . PHP_EOL;
      $str .= '         THEN' . PHP_EOL;
      if($this->canReturnOnInsert())
      {
         $str .= '            UPDATE "' . $tableName . '" SET "' . $column . '" = "' . $tableName . '"."' . $column . '" + 1 WHERE "' . $tableName . '"."' . $where . '" = "' . $where . '" RETURNING "' . $tableName . '"."' . $column . '" INTO "' . $column . '";' . PHP_EOL;
      }
      else
      {
         $str .= '            UPDATE "' . $tableName . '" SET "' . $column . '" = LAST_INSERT_ID("' . $tableName . '"."' . $column . '" + 1) WHERE "' . $tableName . '"."' . $where . '" = "' . $where . '";' . PHP_EOL;
         $str .= '            ' . $this->getSetStr($column, "LAST_INSERT_ID()") . ';' . PHP_EOL;
      }
      $str .= '         ELSE' . PHP_EOL;
      $str .= '            ' . $this->getProcLockString(array($tableName, $tableName . 'S')) . ';' . PHP_EOL;
      $str .= '            INSERT INTO "' . $tableName . '" (' . implode(', ', $varList) . ') VALUES (' . implode(', ', $valuesList) . ')' . ($this->canReturnOnInsert() ? ' RETURNING "' . $tableName . '"."Id" INTO "Id"' : '') . ';' . PHP_EOL;
      if(!$this->canReturnOnInsert())
         $str .= '            ' . $this->getSetStr("Id", "LAST_INSERT_ID()") . ';' . PHP_EOL;

      $str .= '            ' . $this->getSetStr($column, 1) . ';' . PHP_EOL;

      $str .= '         END IF;' . PHP_EOL;
      $str .= '      END IF;' . PHP_EOL;

      if($this->canReturnValues())
         $str .= '      RETURN "' . $column . '";' . PHP_EOL;
      else
         $str .= '      ' . $this->getSetSessionVarStr("@RET", '"' . $column . '"') . ';' . PHP_EOL;

      $str .= '   ' . $this->getProcUnlockString(array($tableName, $tableName . 'S')) . PHP_EOL;
      $str .= $this->getProcedureEndStr();
      return $str;
   }

   protected function getShutdownPriority($tableName)
   {
      static $nextShutdownPriority = 0;
      static $shutdownPriorities = array();
      if(isset($shutdownPriorities[$tableName]))
         return $shutdownPriorities[$tableName];

      $overrides = $this->getSqlOverride($tableName);

      foreach($overrides['Refs'] as $ref => $refTables)
      {
         foreach($refTables as $refTable)
         {
            if($refTable === $tableName)
               continue;

            $this->getShutdownPriority($refTable);
         }
      }

      $shutdownPriorities[$tableName] = $nextShutdownPriority++;

      return $shutdownPriorities[$tableName];
   }

   protected function getSqlOverride($tableName)
   {
      static $sqlOverrides = array();
      if(isset($sqlOverrides[$tableName]))
         return $sqlOverrides[$tableName];

      $overrides = getOverride("MODEL_SQL_OVERRIDES", $tableName, array());
      if(!isset($overrides['Refs']))
         $overrides['Refs'] = array();
      foreach($overrides['Refs'] as $ref => &$tables)
      {
         if(!is_array($tables))
            $tables = array($tables => $tables);
      }
      if(!isset($overrides['Triggers']))
         $overrides['Triggers'] = array();
      foreach($overrides['Triggers'] as $ref => &$tables)
      {
         if(!is_array($tables))
            $tables = array($tables => $tables);
      }
      if(!isset($overrides['Condition']))
         $overrides['Condition'] = array();

      $sqlOverrides[$tableName] = $overrides;
      return $sqlOverrides[$tableName];
   }

   protected function switchOutContent($filePath, $fullString, $callParentCtor = false)
   {
      $snipText = "//%%--SNIP--DO-NOT-REMOVE--SNIP--%%";
      $userContent = "";
      $newBlankContent = "$snipText\n\n   // User created mothods and members go in here \n\n";
      $newBlankContent .= "   // Constructor" . PHP_EOL;
      $newBlankContent .= '   public function __construct(array $options = null)' . PHP_EOL;
      $newBlankContent .= "   {" . PHP_EOL;
      if($callParentCtor)
         $newBlankContent .= '      parent::__construct($options);' . PHP_EOL;
      $newBlankContent .= "   }" . PHP_EOL;
      $newBlankContent .= "\n$snipText";
      if(file_exists($filePath))
      {
         $oldContent = file_get_contents($filePath);
         $firstSnip = strpos($oldContent, $snipText);
         $secondSnip = strpos($oldContent, $snipText, $firstSnip + strlen($snipText) + 1);
         if($firstSnip !== false && $secondSnip !== false)
         {
            $userContent = $snipText;
            $userContent .= substr($oldContent, $firstSnip + strlen($snipText) , $secondSnip - $firstSnip - strlen($snipText));
            $userContent .= $snipText;
         }
         else
         {
            $userContent = $newBlankContent;
         }
      }
      else
      {
         $userContent = $newBlankContent;
      }
      
      return str_replace("%%SNIPHERE%%", $userContent, $fullString);
      
   }

   protected function getLockDependencies($tableName)
   {
      $overrides = $this->getSqlOverride($tableName);

      $ret = array();
      foreach($overrides["Triggers"] as $ref => $tables)
      {
         $condition = isset($overrides["Condition"][$ref]) ? $overrides["Condition"][$ref] : null;
         foreach($tables as $conditionField => $table)
            $ret[] = array('Table' => $table, 'Condition' => $condition, 'ConditionField' => $conditionField, 'Ref' => $ref);
      }

      return $ret;
   }

   protected function getLockDependenciesStr($depList)
   {
      $lockStr = '';
      foreach($depList as $dep)
      {
         $pad = '';
         if($dep["Condition"])
         {
            $pad .= '   ';
            $lockStr .= '      if($pModel->get' . $dep['Condition'] . '() === "' . $dep["ConditionField"] . '")' . PHP_EOL;
            $lockStr .= '      {' . PHP_EOL;
         }
         $lockStr .= $pad . '      $pMapper = new Application_Model_' . ucfirst($this->makeLowercamelcase($dep["Table"])) . 'Mapper();' . PHP_EOL;
         $lockStr .= $pad . '      $pRefModel = $pMapper->fetchById($pModel->get' . $dep['Ref'] . '());' . PHP_EOL;
         $lockStr .= $pad . '      if($pRefModel)' . PHP_EOL;
         $lockStr .= $pad . '         $pMapper->lock($pRefModel);' . PHP_EOL;
         if($dep["Condition"])
            $lockStr .= '      }' . PHP_EOL;

         //$lockStr .= $str;
      }

      return $lockStr;
   }

   protected function getTouchRecursive($tableName, $condition = null, $conditionField = null, $self = false)
   {
      $overrides = $this->getSqlOverride($tableName);

      $touchRecursive = array(array('Table' => $tableName, 'Condition' => $condition, 'ConditionField' => $conditionField, 'Self' => $self));
      foreach($overrides["Triggers"] as $ref => $tables)
      {
         $condition = isset($overrides["Condition"][$ref]) ? $overrides["Condition"][$ref] : null;
         foreach($tables as $conditionField => $table)
            $touchRecursive = array_merge($touchRecursive, $this->getTouchRecursive($table, $condition, $conditionField));
      }

      return $touchRecursive;
   }

   protected function getTouchStr($touchList, $operation)
   {
      $touchStr = '';
      foreach($touchList as $touch)
      {
         if($touch["Condition"])
            $touchStr .= '      if($data["' . $touch["Condition"] . '"] === "' . $touch['ConditionField'] . '")' . PHP_EOL . '   ';

         if($touch['Self'])
            $str = '      self::touch("' . $touch['Table'] . '", "' . $operation . '", $data["Id"]' . ($operation === "Delete" ? ', $fullModel' : '') . ');' . PHP_EOL;
         else
            $str = '      self::touch("' . $touch['Table'] . '", "Modify", $data["' . $touch['Table'] . 'Id"]);' . PHP_EOL;
         $touchStr .= $str;
      }

      return $touchStr;
   }

   protected function makeDbModelFileString($tableName, $tableDesc, $strPad, $versioned, $overrides)
   {
      $extends = getOverride('EXTENDS', "DbTable_" . ucfirst($this->makeLowercamelcase($tableName)), 'Application_Model_DbTable_Base');   
      $dataFieldList = array();
      $placeHolderList = array();
      foreach($tableDesc as $column)
      {
         $dataFieldList[] = '$data["' . $column["Field"] . '"]';
         $placeHolderList[] = "?";
      }

      

      $insertCall = '$dbAdapter->getProcCallStr() . " ' . $tableName . 'Insert(' . implode(', ', $placeHolderList) . ')"';
      $updateCall = '$dbAdapter->getProcCallStr() . " ' . $tableName . 'Update(' . implode(', ', $placeHolderList) . ')"';
      $deleteCall = '$dbAdapter->getProcCallStr() . " ' . $tableName . 'Delete(" . $id . ");"';
      $touchList = $this->getTouchRecursive($tableName, null, null, true);

      $str  = '<?php' . PHP_EOL;
      $str .= generateCopyright() . PHP_EOL;
      $str .= "class Application_Model_DbTable_" .  ucfirst($this->makeLowercamelcase($tableName)) . ' extends ' . $extends . PHP_EOL;
      $str .= '{' . PHP_EOL;
      $str .= '   protected $_name = ' . "'$tableName';" . PHP_EOL;
      $str .= '   protected static $_versioned = ' . ($versioned ? 'true' : 'false') . ';' . PHP_EOL;
      $str .= '   protected static $_priority = ' . $this->getShutdownPriority($tableName) . ';' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '   public static function instance()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      static $me = null;' . PHP_EOL;
      $str .= '      if(!$me)' . PHP_EOL;
      $str .= '         $me = new Application_Model_DbTable_' . ucfirst($this->makeLowercamelcase($tableName)) . '();' . PHP_EOL;
      $str .= '      return $me;' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= '   public static function priority()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      return self::$_priority;' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= '   public static function versioned()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      return self::$_versioned;' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= '   public function insert(array $data)' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      if(isset($data["Operation"]) && $data["Operation"])' . PHP_EOL;
      $str .= '         return parent::insert($data);' . PHP_EOL;
      $str .= '      $dbAdapter = $this->getAdapter();' . PHP_EOL;
      $str .= '      $sql = ' . $insertCall . ';' . PHP_EOL;
      $str .= '      $dataSql = array(' . implode(", ", $dataFieldList) . ');' . PHP_EOL;
      $str .= '      $id = $dbAdapter->callWithReturn($sql, $dataSql);' . PHP_EOL;
      $str .= '      $data["Id"] = $id;' . PHP_EOL;
      $str .= $this->getTouchStr($touchList, "Create");
      $str .= '      return $data["Id"];' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= '   public function update(array $data)' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      if(isset($data["Operation"]) && $data["Operation"])' . PHP_EOL;
      $str .= '         return parent::update($data, \'"Id" = \' . $data["Id"]);' . PHP_EOL;
      $str .= '      $dbAdapter = $this->getAdapter();' . PHP_EOL;
      $str .= '      $sql = ' . $updateCall . ';' . PHP_EOL;
      $str .= '      $dataSql = array(' . implode(", ", $dataFieldList) . ');' . PHP_EOL;
      $str .= '      $dbAdapter->query($sql, $dataSql);' . PHP_EOL;
      $str .= $this->getTouchStr($touchList, "Modify");
      $str .= '      return $data["Id"];' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '   public function callShutdown()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      if($versioned)
      {
         $str .= '      $dbAdapter = $this->getAdapter();' . PHP_EOL;
         $str .= '      $sql = $dbAdapter->getProcCallStr() . " ' . $tableName . 'Shutdown()"; ' . PHP_EOL;
         $str .= '      $dbAdapter->query($sql);' . PHP_EOL;
      }
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '   public function delete(array $data, array $fullModel)' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      if(isset($data["Operation"]) && $data["Operation"])' . PHP_EOL;
      $str .= '         return parent::delete($data["Id"]);' . PHP_EOL;
      $str .= '      $dbAdapter = $this->getAdapter();' . PHP_EOL;
      $str .= '      $id = $data["Id"];' . PHP_EOL;
      $str .= '      $sql = ' . $deleteCall . ';' . PHP_EOL;
      $str .= '      $dbAdapter->query($sql);' . PHP_EOL;
      $str .= $this->getTouchStr($touchList, "Delete");
      $str .= '      return $id;' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      if(isset($overrides["Increment"]))
      {
         foreach($overrides["Increment"] as $incrementProc)
         {
            $str .= '   public function incrementBy' . $incrementProc["Where"] . '($' . $incrementProc["Where"] . ')' . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      $dbAdapter = $this->getAdapter();' . PHP_EOL;
            $str .= '      $sql = $dbAdapter->getProcCallStr() . " ' . $tableName . 'IncrementBy' . $incrementProc["Where"] . '(?)";' . PHP_EOL;
            $str .= '      $dataSql = array($' . $incrementProc["Where"] . ');' . PHP_EOL;
            $str .= '      return $dbAdapter->callWithReturn($sql, $dataSql);' . PHP_EOL;
            $str .= '   }' . PHP_EOL;
         }
      }
      $str .= '%%SNIPHERE%%'. PHP_EOL;
      $str .= '}' . PHP_EOL;
      return $str;
   }

   protected function makeModelMapperFileString($tableName, $tableDesc, $strPad)
   {
      global $defaultModelMapperExtends;
      $extends = getOverride('EXTENDS', ucfirst($this->makeLowercamelcase($tableName)) . 'Mapper', $defaultModelMapperExtends);
      $extendsReplaced = false;
      if($extends != $defaultModelMapperExtends)
         $extendsReplaced = true;

      $str  = '<?php' . PHP_EOL;
      $str .= generateCopyright() . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* Application_Model_' . ucfirst($this->makeLowercamelcase($tableName)) . 'Mapper                                          */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= "class Application_Model_" . ucfirst($this->makeLowercamelcase($tableName)) . "Mapper extends $extends" . PHP_EOL;
      $str .= '{' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* User Defined Functions                                                                         */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '%%SNIPHERE%%'. PHP_EOL;
      $str .= PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* Helper Functions                                                                               */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '   public function modelName()   { return "' . $this->makeLowercamelcase($tableName) . '"; }' . PHP_EOL;
      $str .= PHP_EOL;
      
      $str .= PHP_EOL;
      $str .= '   protected function setRow($row, Application_Model_' . ucfirst($this->makeLowercamelcase($tableName))  . ' & $model, $extraFields = false)' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      foreach($tableDesc as &$row)
      {
         $str .= '      if(isset($row->' . $row["Field"] . ')) $model->set' . $row["PascalCase"] . '($row->' . $row["Field"] . ');' . PHP_EOL;
      }
      $str .= '      parent::setRow($row, $model, $extraFields);' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;

      $str .= PHP_EOL;
      $str .= '   public function lockDependencies(Application_Model_' . ucfirst($this->makeLowercamelcase($tableName))  . ' $pModel)' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .=  $this->getLockDependenciesStr($this->getLockDependencies($tableName));
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '   public function getDataAsArray(Application_Model_' . ucfirst($this->makeLowercamelcase($tableName))  . ' & $model, $forPdo = false)' . PHP_EOL . '   {' . PHP_EOL;
      $str .= '      $data = array(' . PHP_EOL;
      foreach($tableDesc as &$row)
         $str .= "         '" . $row["Field"]  . "'" . substr($strPad, strlen($row["Field"])) . " => " . '$model->get' . $row["PascalCase"] . '($forPdo),' . PHP_EOL;
      $str .= '      );' . PHP_EOL . PHP_EOL;
      $str .= '      $this->postGetDataAsArray($data, $model);' . PHP_EOL;
      $str .= '      return $data;' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= '   public function getDbTable()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      return Application_Model_DbTable_' . ucfirst($this->makeLowercamelcase($tableName)) . '::instance();' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* Parent Class Overrides                                                                         */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;

/*    NOTE(Keith): This needs to be updated to support the new history system
      $str .= '   public function fetchById($id) '. PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      $p' . $tableName . ' = null;' . PHP_EOL;
      $str .= '      // Check to see if we should run this function'. PHP_EOL;
      $str .= '      if(!$this->localFetchById($id, $p' . $tableName . '))'. PHP_EOL;
      $str .= '         return $p' . $tableName . ';'. PHP_EOL;
      $str .= '      ' . PHP_EOL;
      $str .= '      $p' . $tableName . ' = $this->loadFromCache($this->getKeyNameById($id));' . PHP_EOL;
      $str .= '      // ' . $tableName . ' found in cache' . PHP_EOL;
      $str .= '      if($p' . $tableName . ')' . PHP_EOL;
      $str .= '         return $p' . $tableName . ';' . PHP_EOL;
      $str .= '      ' . PHP_EOL;
      $str .= '      // ' . $tableName . ' not found in cache, look it up in the database' . PHP_EOL;
      $str .= '      $p' . $tableName . ' = parent::fetchById($id);' . PHP_EOL;
      $str .= '      // Save the result in the cache so future lookup events will find it' . PHP_EOL;
      $str .= '      $this->saveToCache($p' . $tableName . ', $this->getKeyNameById($id));' . PHP_EOL;
      $str .= '      ' . PHP_EOL;
      $str .= '      return $p' . $tableName . ';' . PHP_EOL;
      $str .= '   }' . PHP_EOL; */
      $str .= '}' . PHP_EOL;
      $str .= PHP_EOL;
      return $str;
   }

   protected function makeModelFileString($tableName, $tableDesc, $strPad)
   {
      global $defaultModelExtends;
      $extends = getOverride('EXTENDS', ucfirst($this->makeLowercamelcase($tableName)), $defaultModelExtends);
      $modelPropsInit = '   public static $modelProps = array(';
      $modelProps = $modelPropsInit;
      
      $str  = '<?php' . PHP_EOL;
      $str .= generateCopyright() . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* Application_Model_' . ucfirst($this->makeLowercamelcase($tableName)) . '                                                */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= "class Application_Model_" . ucfirst($this->makeLowercamelcase($tableName)) . " extends $extends" . PHP_EOL;
      $str .= '{' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* Programmer Defined Functions                                                                   */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '%%SNIPHERE%%'. PHP_EOL;
      $str .= PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* Member Variables                                                                               */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= PHP_EOL;
      foreach($tableDesc as &$row)
      {
         if($modelProps != $modelPropsInit)
            $modelProps .= ', ';
         $modelProps .= '\'' . $row["PascalCase"] . '\'';
         $str        .= '   protected $_' . $row["camelCase"]  . ';' . PHP_EOL;
      }
      $str .= PHP_EOL . $modelProps . ');' . PHP_EOL;
      $str .= '   protected static $allModelProps = null;' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* Helper Functions                                                                               */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= PHP_EOL;
      $str .= '   public function modelName()   { return "' . $this->makeLowercamelcase($tableName) . '"; }' . PHP_EOL;
      $str .= PHP_EOL;
      
      $str .= PHP_EOL;
      $str .= '   public static function mapper()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      return new Application_Model_' . ucfirst($this->makeLowercamelcase($tableName)) . 'Mapper();' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;

      $str .= PHP_EOL;
      $str .= '   public function mapperInst()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      return new Application_Model_' . ucfirst($this->makeLowercamelcase($tableName)) . 'Mapper();' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;

      $str .= PHP_EOL;
      $str .= '   public function save()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      return self::mapper()->save($this);' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;

      $str .= PHP_EOL;
      $str .= '   public function delete()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      return self::mapper()->delete($this);' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;

      $str .= PHP_EOL;
      $str .= '   public static function modelProps()' . PHP_EOL;
      $str .= '   {' . PHP_EOL;
      $str .= '      if(self::$allModelProps === null)' . PHP_EOL;
      $str .= '         self::$allModelProps = array_unique(array_merge(Application_Model_' . ucfirst($this->makeLowercamelcase($tableName)) . '::$modelProps, parent::modelProps()));' . PHP_EOL;
      $str .= '      return self::$allModelProps;' . PHP_EOL;
      $str .= '   }' . PHP_EOL;
      $str .= PHP_EOL;
      
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= '/* Getters, Setters, and ENUMs                                                                    */' . PHP_EOL;
      $str .= '/**************************************************************************************************/' . PHP_EOL;
      $str .= PHP_EOL;

      $postSets = getOverride('MODEL_POST_SETS', ucfirst($this->makeLowercamelcase($tableName)), array());
      $postGets = getOverride('MODEL_POST_GETS', ucfirst($this->makeLowercamelcase($tableName)), array());
      $setOverrides = getOverride('MODEL_SET_OVERRIDES', ucfirst($this->makeLowercamelcase($tableName)), array());
      $getOverrides = getOverride('MODEL_GET_OVERRIDES', ucfirst($this->makeLowercamelcase($tableName)), array());
      $jsonMemberOverrides = getOverride('MODEL_MEMBER_USES_JSON', ucfirst($this->makeLowercamelcase($tableName)), array());
      
      foreach($tableDesc as &$row)
      {
         $rhsAssignmentValue = '$rhs';
         $enumList = array();
         $setList = array();
         $boolean = false;
         $integer = false;
         $binary = false;
         if(strpos($this->getSQLType($row), 'binary') !== false)
         {
            $binary = true;
         }
         else if($this->getSQLType($row) === "tinyint(1)")
         {
            $boolean = true;
         }
         else if(strpos($this->getSQLType($row), 'int') !== false)
         {
            $integer = true;
         }
         else if(substr($this->getSQLType($row), 0, 4) == 'enum')
         {
            $listAsString = substr($this->getSQLType($row), 5, -1);
            $list = explode(",", $listAsString);
            foreach($list as $item)
            {
               // Remove the "'"
               $item = substr($item, 1, -1);
               $enumList[$item] = strtoupper($row["PascalCase"] . '__' . $this->makeUnderscoreFromCamelcase($item));
            }
         }
         else if(substr($this->getSQLType($row), 0, 3) == 'set')
         {
            $listAsString = substr($this->getSQLType($row), 4, -1);
            $list = explode(",", $listAsString);
            foreach($list as $item)
            {
               // Remove the "'"
               $item = substr($item, 1, -1);
               $setList[$item] = strtoupper($row["PascalCase"] . '__' . $this->makeUnderscoreFromCamelcase($item));
            }
         }

         // ENUM VALUES
         $enumArrayVarName = '$s' . ucfirst($row["PascalCase"] . 'EnumArray');
         $enumArrayStr = 'public static ' . $enumArrayVarName . ' = array(';
         if(count($enumList) > 0)
            $str .= '   // ENUM values for ' . $row["PascalCase"] . PHP_EOL;
         foreach($enumList as $key => $value)
         {
            $str .= '   const ' . $value . ' = \'' . $key . '\';' . PHP_EOL;
            $enumArrayStr .= PHP_EOL . '      \'' . $value . '\'  => self::' .  $value;
            end($enumList);
            if(key($enumList) != $key)
               $enumArrayStr .= ',';
         }
         $enumArrayStr .= PHP_EOL . '   );';
         if(count($enumList) > 0)
            $str .= '   ' . $enumArrayStr . PHP_EOL . PHP_EOL;

         // SET VALUES
         $setArrayVarName = '$s' . ucfirst($row["PascalCase"] . 'SetArray');
         $setArrayStr = 'public static ' . $setArrayVarName . ' = array(';
         if(count($setList) > 0)
            $str .= '   // SET values for ' . $row["PascalCase"] . PHP_EOL;
         foreach($setList as $key => $value)
         {
            $str .= '   const ' . $value . ' = \'' . $key . '\';' . PHP_EOL;
            $setArrayStr .= PHP_EOL . '      \'' . $value . '\'  => self::' .  $value;
            end($setList);
            if(key($setList) != $key)
               $setArrayStr .= ',';
         }
         $setArrayStr .= PHP_EOL . '   );';
         if(count($setList) > 0)
            $str .= '   ' . $setArrayStr . PHP_EOL . PHP_EOL;
         
         $str .= '   // Get/Set for ' . $row["PascalCase"] . PHP_EOL;
         // GET
         if(in_array($row["PascalCase"], $getOverrides))
         {
            $str .= '   // overrides directive: function "get' . $row["PascalCase"] . '" was not generated' . PHP_EOL;
         }
         else if(in_array($row["PascalCase"], $jsonMemberOverrides) && count($enumList) == 0)
         {
            $str .= '   protected $mDecoded' . $row["camelCase"] . ' = null;' . PHP_EOL;
            $str .= '   public function get' . $row["PascalCase"] . '($forPdo = false)' . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      if($forPdo)' . PHP_EOL;
            $str .= '      {' . PHP_EOL;
            $str .= '         if($this->_' . $row["camelCase"] . ' === null && $this->mDecoded' . $row["camelCase"] . ' !== null)' . PHP_EOL;
            $str .= '            $this->_' . $row["camelCase"] . ' = json_encode($this->mDecoded' . $row["camelCase"] . ');' . PHP_EOL;
            $str .= '         return $this->_' . $row["camelCase"] . ';' . PHP_EOL;
            $str .= '      }' . PHP_EOL;
            $str .= '' . PHP_EOL;
            $str .= '      if($this->mDecoded' . $row["camelCase"] . ' === null && $this->_' . $row["camelCase"] . ' !== null)' . PHP_EOL;
            $str .= '         $this->mDecoded' . $row["camelCase"] . ' = json_decode($this->_' . $row["camelCase"] . ', true);' . PHP_EOL;
            $str .= '' . PHP_EOL;
            $str .= '      return $this->mDecoded' . $row["camelCase"] . ';' . PHP_EOL;
            $str .= '   }' . PHP_EOL;
         }
         else if(count($setList) > 0)
         {
            $str .= '   public function get' . $row["PascalCase"] . '($forPdo = false)' . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      if(!$forPdo)' . PHP_EOL;
            $str .= '         return $this->_' . $row["camelCase"] .';' . PHP_EOL;
            $str .= '      $returnStr = "";' . PHP_EOL;
            $str .= '      foreach($this->_' . $row["camelCase"] .' as $key => $value)' . PHP_EOL;
            $str .= '      {' . PHP_EOL;
            $str .= '         if($key != 0)' . PHP_EOL;
            $str .= '            $returnStr .= ",";' . PHP_EOL;
            $str .= '         $returnStr .= $value;' . PHP_EOL;
            $str .= '      }' . PHP_EOL;
            $str .= '      return $returnStr;' . PHP_EOL;
            $str .= '   }' . PHP_EOL;
         }
         else if($boolean)
         {
            $str .= '   public function get' . $row["PascalCase"] . '($forPdo = false)' . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      if(!$forPdo)' . PHP_EOL;
            $str .= '         return $this->_' . $row["camelCase"] .';' . PHP_EOL;
            $str .= '      return $this->_' . $row["camelCase"] .' ? 1 : 0;' . PHP_EOL;
            $str .= '   }' . PHP_EOL;
         }
         else if($integer)
         {
            $str .= '   public function get' . $row["PascalCase"] . '($forPdo = false)' . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      return $this->_' . $row["camelCase"] . ' === null ? null : intval($this->_' . $row["camelCase"] . ');' . PHP_EOL;
            $str .= '   }' . PHP_EOL;
         }
         //TODO fix this for postgres since binary values are returned to the PM as resources.. doesn't matter yet since theyre only used for guids which dont change.
         else if($binary && false)
         {
            $str .= '   public function get' . $row["PascalCase"] . '($forPdo = false)' . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      return $forPdo && is_resource($this->_' . $row["camelCase"] . ') ? stream_get_contents($this->_' . $row["camelCase"] . ') : $this->_' . $row["camelCase"] . ';' . PHP_EOL;
            $str .= '   }' . PHP_EOL;
         }
         else
         {
            $append = '';
            if(in_array($row["PascalCase"], $postGets))
               $append = '$this->_postGet' . $row["PascalCase"] . '()';
            $str .= '   public function get' . $row["PascalCase"] . '($forPdo = false)    ' . substr($strPad, strlen($row["PascalCase"])) . '{ return $this->_' . $row["camelCase"] .';  ' . $append . '}' . PHP_EOL;
         }
         
         // SET
         if(in_array($row["PascalCase"], $setOverrides))
         {
            $str .= '   // overrides directive: function "set' . $row["PascalCase"] . '" was not generated' . PHP_EOL;
         }
         else if(in_array($row["PascalCase"], $jsonMemberOverrides) && count($enumList) == 0)
         {
            $str .= '   public function set' . $row["PascalCase"] . '($rhs)' . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      if($rhs === null)' . PHP_EOL;
            $str .= '      {' . PHP_EOL;
            $str .= '         $this->_' . $row["camelCase"] . ' = null;' . PHP_EOL;
            $str .= '         $this->mDecoded' . $row["camelCase"] . ' = null;' . PHP_EOL;
            $str .= '      }' . PHP_EOL;
            $str .= '      else if(is_array($rhs))' . PHP_EOL;
            $str .= '      {' . PHP_EOL;
            $str .= '         $this->_' . $row["camelCase"] . ' = null;' . PHP_EOL;
            $str .= '         $this->mDecoded' . $row["camelCase"] . ' = $rhs;' . PHP_EOL;
            $str .= '      }' . PHP_EOL;
            $str .= '      else' . PHP_EOL;
            $str .= '      {' . PHP_EOL;
            $str .= '         $this->_' . $row["camelCase"] . ' = $rhs;' . PHP_EOL;
            $str .= '         $this->mDecoded' . $row["camelCase"] . ' = null;' . PHP_EOL;
            $str .= '      }' . PHP_EOL;
            $str .= '   }' . PHP_EOL;
         }
         else if($boolean)
         {
            $str .= '   public function set' . $row["PascalCase"] . '($rhs)               ' . substr($strPad, strlen($row["PascalCase"])) . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      $this->_' . $row["camelCase"] . ' = ($rhs && $rhs !== "0") ? true : false;' . PHP_EOL;
            $str .= '   }' . PHP_EOL;

         }
         else if($integer)
         {
            $str .= '   public function set' . $row["PascalCase"] . '($rhs)               ' . substr($strPad, strlen($row["PascalCase"])) . PHP_EOL;
            $str .= '   {' . PHP_EOL;
            $str .= '      $this->_' . $row["camelCase"] . ' = $rhs !== null ? intval($rhs) : $rhs;' . PHP_EOL;
            $str .= '   }' . PHP_EOL;
         }
         else
         {
            $append = '';
            if(in_array($row["PascalCase"], $postSets))
               $append = '$this->_postSet' . $row["PascalCase"] . '()';
            $str .= '   public function set' . $row["PascalCase"] . '($rhs)               ' . substr($strPad, strlen($row["PascalCase"]));
            if(count($setList) > 0)
            {
               $str .= PHP_EOL . '   { ';
               $str .= PHP_EOL . '      if($rhs == "")';
               $str .= PHP_EOL . '         $rhs = array();';
               $str .= PHP_EOL . '      if(is_string($rhs))';
               $str .= PHP_EOL . '         $rhs = explode(",", $rhs);';
               $str .= PHP_EOL . '      if(!is_array($rhs))';
               $str .= PHP_EOL . '         throw(new Exception("Must pass array or string to a model\'s set SET function."));';
               $str .= PHP_EOL . '      foreach($rhs as $value)';
               $str .= PHP_EOL . '      {';
               $str .= PHP_EOL . '         if(!in_array($value, self::' . $setArrayVarName . '))';
               $str .= PHP_EOL . '            throw(new Exception(\'The value "\' . $value . \'" can not be assigned to SET value beacuse the SET does not define such a value.\'));';
               $str .= PHP_EOL . '      }';
               $str .= PHP_EOL . '      ';
               $str .= '$this->_' . $row["camelCase"] .' = ' . $rhsAssignmentValue . '; ' . $append;
               $str .= PHP_EOL . '   }' . PHP_EOL;
               $str .= '   public function addTo' . $row["PascalCase"] . 'Set($rhs)' . PHP_EOL;
               $str .= '   { ' . PHP_EOL;
               $str .= '      if(!in_array($rhs, self::' . $setArrayVarName . '))' . PHP_EOL;
               $str .= '         throw(new Exception(\'The value "\' . ' . $rhsAssignmentValue . ' . \'" can not be assigned to SET value beacuse the SET does not define such a value.\'));' . PHP_EOL;
               $str .= '      $this->_' . $row["camelCase"] . '[] = $rhs;'. PHP_EOL;
               $str .= '      $this->_' . $row["camelCase"] . ' = array_unique($this->_' . $row["camelCase"] . ');' . PHP_EOL;
               $str .= '   } ' . PHP_EOL;
               $str .= '   public function removeFrom' . $row["PascalCase"] . 'Set($rhs)' . PHP_EOL;
               $str .= '   { ' . PHP_EOL;
               $str .= '      if(!in_array($rhs, self::' . $setArrayVarName . '))' . PHP_EOL;
               $str .= '         throw(new Exception(\'The value "\' . ' . $rhsAssignmentValue . ' . \'" can not be assigned to SET value beacuse the SET does not define such a value.\'));' . PHP_EOL;
               $str .= '      if(($key = array_search($rhs, $this->_' . $row["camelCase"] . ')) !== false)' . PHP_EOL;
               $str .= '          unset($this->_' . $row["camelCase"] . '[$key]);' . PHP_EOL;
               $str .= '   } ' . PHP_EOL;
            }
            else
            {
               if(count($enumList) > 0)
                  $str .= PHP_EOL . '   ';
               $str .= '{ ';
               if(count($enumList) > 0)
               {
                  $str .= PHP_EOL . '      if($rhs != \'\' && !in_array($rhs, self::' . $enumArrayVarName . '))';
                  $str .= PHP_EOL . '         throw(new Exception(\'The value "\' . ' . $rhsAssignmentValue . ' . \'" can not be assigned to ENUM value beacuse the ENUM does not define such a value.\'));';
                  $str .= PHP_EOL . '      ';
               }
               $str .= '$this->_' . $row["camelCase"] .' = ' . $rhsAssignmentValue . '; ' . $append;
               if(count($enumList) > 0)
                  $str .= PHP_EOL . '   ';
               $str .= '}' . PHP_EOL;
            }
         }
         
         $str .= PHP_EOL;
      }
      $str .= '}' . PHP_EOL;
      return $str;
   }

   protected function makeLowercamelcase($str) 
   { 
      $str = str_replace('_', ' ', $str);
      return preg_replace("/\s+([a-z])/ie", 'strtoupper("$1")', trim($str)); 
   }

   protected function makeUnderscoreFromCamelcase($str)
   {
      return strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $str));
   }
}

class MysqlGenerator extends Generator
{
   static protected $_sqlModeString = "SET sql_mode = 'ANSI_QUOTES';";

   public function __construct($dbAdapter, $tableName = null)
   {
      parent::__construct($dbAdapter, $tableName);
      $this->_dbAdapter->query(self::$_sqlModeString);
   }

   protected function getSQLType($column)
   {
      switch($column["Type"]){
         case "int":
            $M = $column["TypeModifiers"]["M"];
            switch($M){
               case ($M < 4):
                  $tempType = "tinyint($M)";
                  break;
               case ($M < 6):
                  $tempType = "smallint($M)";
                  break;
               case ($M < 8):
                  $tempType = "mediumint($M)";
                  break;
               case ($M < 11):
                  $tempType = "int($M)";
                  break;
               default:
                  $tempType = "bigint($M)";
                  break;
            }
            if ($column["TypeModifiers"]["Unsigned"] == true){
            if ($M == 8)
               $tempType = "mediumint($M)";
               $tempType .= " unsigned";
            }
            break;
         case "text":
            if (isset($column["TypeModifiers"]["MySQLType"]))
               $tempType = $column["TypeModifiers"]["MySQLType"];
            else
               $tempType = "text";
            break;
         case "enum":
            $tempType = "enum(";
            foreach ($column["TypeModifiers"]["Values"] as $enumVal){
               $tempType .= "'$enumVal',";
            }
            $tempType = substr($tempType, 0, -1);
            $tempType .= ")";
            break;
         case "timestamp":
            if (isset($column["TypeModifiers"]["MySQLType"]))
               $tempType = $column["TypeModifiers"]["MySQLType"];
            else
               $tempType = "timestamp";
            break;
         case "varchar":
            $tempType = "varchar({$column['TypeModifiers']['M']})";
            break;
         case "bit":
            $tempType = "binary({$column['TypeModifiers']['M']})";
            break;
         case "char":
            $tempType = "char({$column['TypeModifiers']['M']})";
            break;
         default:
            $tempType = $column["Type"];
            break;
      }
      return $tempType;
   }

   protected function saveProceduresToFile()
   {
      $beginDelimiterString = 'DELIMITER $$' . PHP_EOL;
      $endDelimiterString = 'DELIMITER ;' . PHP_EOL;
      $sqlFileString = $this->_sqlDropString . PHP_EOL . self::$_sqlModeString . PHP_EOL . $beginDelimiterString . $this->_sqlProcString . $endDelimiterString;
      file_put_contents('./pmMysqlProcedures.sql', $sqlFileString);
      file_put_contents('./pmMysqlDropProcedures.sql', $this->_sqlDropString);
   }

   protected function fixProcStringForQuery($procString)
   {

      return str_replace(array("$$", "DELIMITER ;", "DELIMITER $$"), array(";", "", ""), $procString);
   }

   protected function getParamList($tableDesc, array $include = null, array $exclude = null)
   {
      $paramList = parent::getParamList($tableDesc, $include, $exclude);
      foreach($paramList as &$param)
         $param = 'IN ' . $param;

      return $paramList;
   }

   protected function getDeclareList($tableDesc, array $include = null, array $exclude = null)
   {
      $declareList = parent::getDeclareList($tableDesc, $include, $exclude);
      foreach($declareList as &$declare)
         $declare = 'DECLARE ' . $declare;

      return $declareList;
   }

   protected function getTableDesc($tableName)
   {
      $tableDesc = $this->createTableDesc($tableName); 
      $largestFieldCount = 0;
      $versioned = false;
      foreach($tableDesc as &$column)
      {
         $column["camelCase"]    = $this->makeLowercamelcase($column["Field"]);
         $column["PascalCase"]   = ucfirst($column["camelCase"]);
         if(strlen($column["Field"]) > $largestFieldCount)
            $largestFieldCount = strlen($column["Field"]);

         if($column["Field"] === "PrimeId")
            $versioned = true;
      }
      $strPad = '';
      for($i = 0; $i < $largestFieldCount + ($largestFieldCount%3); $i++)
         $strPad .= ' ';

      return array('desc' => $tableDesc, 'versioned' => $versioned, 'pad' => $strPad); 
   }

   protected function createModelsSupported()
   {
      return true;
   }

   protected function makeDropProcedure($tableName, $tableDesc, $funcName)
   {
      return 'DROP PROCEDURE IF EXISTS ' . $tableName . $funcName . ';' . PHP_EOL;
   }

   protected function getProcedureBeginStr($tableName, $funcName, $paramList, $returnType = null, $declareList = array())
   {
      $str  = 'CREATE PROCEDURE ' . $tableName . $funcName . '(' . implode(', ', $paramList) . ')' . PHP_EOL;
      $str .= '   BEGIN' . PHP_EOL;
      $str .= $this->getDeclareStr($declareList);
      return $str;
   }

   protected function getProcedureEndStr()
   {
      $str = '   END $$' . PHP_EOL;
      $str .= PHP_EOL;
      return $str;
   }

   protected function getNowFuncStr()
   {
      return "utc_timestamp()";
   }

   protected function getUuidStr()
   {
      return "UNHEX(REPLACE(UUID(),'-',''))";
   }

   protected function getSetStr($varName, $value, $quoteIntoVarName = true)
   {
      return 'SET ' . parent::getSetStr($varName, $value, $quoteIntoVarName);
   }

   protected function getSetSessionVarStr($varName, $value)
   {
      return 'SET ' . $varName . ' = ' . $value;
   }

   protected function getCurrentUserStr()
   {
      return "@CURRENT_USER";
   }

   protected function getCallStr()
   {
      return "CALL";
   }

   protected function getCopyStr($sourceTable, $targetTable, $tableDesc)
   {
      $varList = $this->getFieldList($tableDesc, $sourceTable);
      $varList[0] = "NULL";
      return 'INSERT INTO "' . $targetTable . '" SELECT ' . implode(', ', $varList) . ' FROM "' . $sourceTable . '";' . PHP_EOL;
   }

   protected function getDeclareStr($declareList)
   {
      if(!count($declareList))
         return '';

      $str = '      ' . implode(';' . PHP_EOL . '      ', $declareList);
      $str .= ';' . PHP_EOL;

      return $str;
   }

   protected function canReturnOnInsert()
   {
      return false;
   }

   protected function canReturnValues()
   {
      return false;
   }

   protected function hasCommaSeparatedUpdateTables()
   {
      return true;
   }

   protected function subQueryCanJoinOnOuterTables()
   {
      return false;
   }

   protected function coalesceNeedsCast()
   {
      return false;
   }

   protected function getProcLockString(array $list)
   {
      return "SELECT GET_LOCK('" . implode('__', $list) . "__LOCK', -1)";
   }

   protected function getProcUnlockString(array $list)
   {
      return "SELECT RELEASE_LOCK('" . implode('__', $list) . "__LOCK');";
   }

}

class PgsqlGenerator extends Generator
{
   public function __construct($dbAdapter, $tableName = null)
   {
      parent::__construct($dbAdapter, $tableName);
   }
   
   protected function getSQLType($column)
   {
      switch($column["Type"])
      {
         case "int":
            $M = $column["TypeModifiers"]["M"];
            switch($M)
            {
               case ($M == 1):
                  $tempType = "boolean";
                  break;
               case ($M < 11):
                  $tempType = "integer";
                  break;
               case ($M < 20):
                  $tempType = "bigint";
                  break;
               default:
                  $tempType = "numeric";
                  break;
            }
            if ($column["TypeModifiers"]["Unsigned"] == true && $M == 10)
                  $tempType = "bigint";
            break;
         case "text":
            $tempType = "text";
            break;
         case "enum":
            $tempType = "character varying";
            $c = 0;
            foreach ($column["TypeModifiers"]["Values"] as $enumVal)
            {
               if ($c < strlen($enumVal))
                  $c = strlen($enumVal);
            }
            $tempType .= "($c)";
            break;
         case "timestamp":
            $tempType = "timestamp without time zone";
            break;
         case "varchar":
            $tempType = "character varying({$column['TypeModifiers']['M']})";
            break;
         case "bit":
            $tempType = "bytea";
            break;
         case "char":
            $tempType = "character({$column['TypeModifiers']['M']})";
            break;
         default:
            $tempType = $column["Type"];
            break;
      }
      return $tempType;
   }

   protected function getTableDesc($tableName)
   {
/*
$sql = 'SELECT column_name AS "Field", data_type AS "Type", character_maximum_length AS "Length"' . " FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '" . $tableName . "'";
$tableDesc = $this->_dbAdapter->fetchAll($sql);
$tableDesc = array_reverse($tableDesc);
*/
      $tableDesc = $this->createTableDesc($tableName); 
      $versioned = false;
      foreach($tableDesc as &$column)
      {

//if ($column["Length"])
//   $column["Type"] = $column["Type"] . '(' . $column["Length"] . ')';

         if($column["Field"] === "PrimeId")
            $versioned = true;
      }
      return array('desc' => $tableDesc, 'versioned' => $versioned);
   }
/*
   protected function getParamList($tableDesc, array $include = null, array $exclude = null)
   {
      $paramList = array();
      foreach($tableDesc as $column)
      {
         $field = $column["Field"];
         if($include && !in_array($field, $include))
            continue;
         if($exclude && in_array($field, $exclude))
            continue;

         $paramList[] = '"' . $field . '" ' . $this->getPgSQLType($column);
      }
      return $paramList;
   } 

   protected function getDeclareList($tableDesc, array $include = null, array $exclude = null)
   {
      $declareList = array();
      foreach($tableDesc as $column)
      {
         $field = $column["Field"];
         if($include && !in_array($field, $include))
            continue;

         if($exclude && in_array($field, $exclude))
            continue;

         $declareList[] = '"' . $field . '" ' . $this->getPgSQLType($column);
      }
      return $declareList;
   }

   protected function getColumnType($tableDesc, $field)
   {
      foreach($tableDesc as $column)
      {
         if($column["Field"] !== $field)
            continue;

         return $this->getPgSQLType($column);
      }

      return null;
   }
*/

   protected function saveProceduresToFile()
   {
      file_put_contents('./pmPgsqlProcedures.sql', $this->_sqlProcString);
      file_put_contents('./pmPgsqlDropProcedures.sql', $this->_sqlDropString);
   }
}

return true;

?>
